#include "common/log.h"
#include "common/flag.h"

#include <cassert>
#include <functional>
#include <iostream>

namespace EoS
{
    typedef TFlag<LogLevel> LevelFlag;

    Log::~Log()
    {
        if(mValid)
        {
            const String& message = mStream.str();
            ::std::lock_guard<decltype(msLoggersGuard)> lock(msLoggersGuard);
            for(Logger* logger : msLoggers)
            {
                logger->Log(mLogLevel, message, mMessageHead);
            }
        }
    }

    void Log::RegisterLogger( Logger* logger )
    {
        ::std::lock_guard<decltype(msLoggersGuard)> lock(msLoggersGuard);
        assert(::std::find(msLoggers.begin(), msLoggers.end(), logger) == msLoggers.end());
        msLoggers.push_back(logger);
    }

    void Log::UnregisterLogger( Logger* logger )
    {
        ::std::lock_guard<decltype(msLoggersGuard)> lock(msLoggersGuard);
        const auto& it = ::std::find(msLoggers.begin(), msLoggers.end(), logger);
        assert(it != msLoggers.end());
        msLoggers.erase(it);
    }

    ::std::vector<Logger*> Log::msLoggers;
    ::std::mutex Log::msLoggersGuard;

    Logger::~Logger()
    {
        Log::UnregisterLogger(this);
    }

    EoS::LogLevel Logger::GetLevel() const
    {
        return mLevel;
    }

    void Logger::Log( const LogLevel level, const String& message, const String& messageHead )
    {
        typedef TFlag<LogLevel, int> Flag;
        Flag myFlag(mLevel);
        if(myFlag.Has(level))
            OnLog(level, message, messageHead);
    }

    void StandartLogger::OnLog( const LogLevel level, const String& message, const String& messageHead )
    {
        switch (level)
        {
        case LogLevel::eFATAL:   { ::std::cerr << "[FATAL ERROR]" << (!messageHead.empty()? " " + messageHead : "") << ": " << message << ::std::endl; break; }
        case LogLevel::eERROR:   { ::std::cerr << "[ERROR]"       << (!messageHead.empty()? " " + messageHead : "") << ": " << message << ::std::endl; break; }
        case LogLevel::eWARNING: { ::std::cout << "[WARNING]"     << (!messageHead.empty()? " " + messageHead : "") << ": " << message << ::std::endl; break; }
        case LogLevel::eINFO:    { ::std::cout << "[INFO]"        << (!messageHead.empty()? " " + messageHead : "") << ": " << message << ::std::endl; break; }
        default:                 { ::std::cout << "[LOG]"         << (!messageHead.empty()? " " + messageHead : "") << ": " << message << ::std::endl; break; }
        }
    }
}